﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace KineticListView
{
    public class KineticScrollViewer : ScrollViewer 
    {
        private Point _mouseDragStartPoint;
        private DateTime _mouseDragStartOperations;
        private Point _scrollStartOffset;
        private bool _isScrolling;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Setup listViewScrollViewer
           HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
           VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
           CanContentScroll = false;  
        }

        protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                _mouseDragStartPoint = e.GetPosition(this);
                _mouseDragStartOperations = DateTime.Now;
                _scrollStartOffset.X = HorizontalOffset;
                _scrollStartOffset.Y = VerticalOffset;
            }

            base.OnPreviewMouseDown(e);
        }

        private bool CanStartScrollOperation(Point position)
        {
            return MouseMovementGreaterThanBias(position, 20, _mouseDragStartPoint);
        }

        private static bool MouseMovementGreaterThanBias(Point position, int biasValue, Point mouseDragStartPoint)
        {
            return (Math.Abs(mouseDragStartPoint.X - position.X) > biasValue)
                     || (Math.Abs(mouseDragStartPoint.Y - position.Y) > biasValue);
        }

        private bool IsMouseActualOver(Point p)
        {
            if ((p.X > 1 && p.X < this.ActualWidth - 1) &&
                 (p.Y > 1 && p.Y < this.ActualHeight - 1))
                return true;
            else
                return false;
        }

        private double GetOffsetY(Point mouseDragCurrentPoint)
        {

                return (mouseDragCurrentPoint.Y > this._mouseDragStartPoint.Y)
                              ? -(mouseDragCurrentPoint.Y - this._mouseDragStartPoint.Y)
                              : (this._mouseDragStartPoint.Y - mouseDragCurrentPoint.Y);

        }

        private double GetOffsetX(Point mouseDragCurrentPoint)
        {
           
                return (mouseDragCurrentPoint.X > this._mouseDragStartPoint.X)
                              ? -(mouseDragCurrentPoint.X - this._mouseDragStartPoint.X)
                              : (this._mouseDragStartPoint.X - mouseDragCurrentPoint.X);

        }

        private void ScrollTheTemplate(Point delta)
        {
          
            ScrollToHorizontalOffset(this._scrollStartOffset.X + delta.X);
            ScrollToVerticalOffset(this._scrollStartOffset.Y + delta.Y);
      
        }

        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            //le operazioni di scroll iniziano solamente se l'offset è maggiore di un certo tot,
            //per evitare di iniziare delle operazioni di microscroll e di evitare che siano premuti i bottoni
            if (e.LeftButton == MouseButtonState.Pressed && !_isScrolling)
            {

                //ho il bottone sinistro premuto, ma non sto ancora scrollando, voglio capire 
                //se ho spostato più di un certo offset.
                var position = e.GetPosition(this);
                if (CanStartScrollOperation(position))
                {
                    _isScrolling = true;
                    
                   // UpdateCursorBasedOnCurrentScrollCapacity();
                }
            }
            //procedo con il drag solamente se sto scrollando
            if (e.LeftButton == MouseButtonState.Pressed && _isScrolling)
            {
                if (IsMouseActualOver(e.GetPosition(this)))
                {
                    if (!(IsMouseCaptured))
                    {
                        this.CaptureMouse();
                    }
                    // Get the new mouse position. 
                    Point mouseDragCurrentPoint = e.GetPosition(this);

                    // Determine the new amount to scroll. 
                    Point delta = new Point(
                         GetOffsetX(mouseDragCurrentPoint),
                         GetOffsetY(mouseDragCurrentPoint));

                    // Scroll to the new position. 
                    ScrollTheTemplate(delta);
                }
                else
                {
                    // Set isScrolling to false and update the cursor
                    this.Cursor = Cursors.Arrow;

                    // Release mouse capture when the mouse is not over the control
                    // did this to disable scrolling while mouse is not over the control
                    if (this.IsMouseCaptured)
                    {
                        this.ReleaseMouseCapture();
                    }
                }
            }
            base.OnPreviewMouseMove(e);
        }
    }
}
